/*
 *  Map.cpp
 *  Untitled
 *
 *  Created by Guo Wei Tan on 1/20/14.
 *  Copyright 2014 Catropico. All rights reserved.
 *
 */

#include "GameMap.h"
#include "GameManager.h"
#include "Obstacle.h"

//===== CONSTRUCTOR =====
GameMap::GameMap()
{
	_scaleFactor = 1;
	_obstacles = 0;
}

void GameMap::init(CCString* pMapImageDirectory, GameManager* screenHolder)
{
	//store reference to canvas
	_screenHolder = screenHolder;

	//store screen size
	_screenSize = CCDirector::sharedDirector()->getWinSize();

	//prepare map ( must be called first)
	setupMapImage(pMapImageDirectory);
	//prepare obstacles
	setupObstacleImage();

	//add background to display
	screenHolder->addChild(_pBgSprite1, 0);
	screenHolder->addChild(_pBgSprite2, 0);

	//preload obstacles sprite
	CCTextureCache::sharedTextureCache()->addImage("Game/Obstacles/[Object] Obstacles (Small).png");
}


//===== FUNCTIONS =====

/*!
    @function setupObstacleImage
    @result		caches all the obstacle sprites.
    			sets up the obstacle holder and tag counter
 */
void GameMap::setupObstacleImage()
{
	//set up obstacles holder
	_obstacles = new CCArray();

	//TODO: Caching of obstacles sprites here
	//load obstacle sprites

}


/*!
    @function setupMapImage
    @param		pMapImageDirectory:CCString
				directory of the first background image
    @result		loads the background stated at the directory and directory+1.
    			Also sets up their starting position.
    			Does not add to canvas.
 */
void GameMap::setupMapImage(CCString* pMapImageDirectory)
{
	//load 1 background image first (anchor point is bottom left)
	_pBgSprite1 = CCSprite::create(pMapImageDirectory->getCString());

	//calculate scale factor based on height restriction
	_scaleFactor = ((float)_screenSize.height) / _pBgSprite1->boundingBox().size.height;

	//set the scale factor
	_pBgSprite1->setScale(_scaleFactor);

	//pre-calculate right bound for scrolling background
	_bgEndPos = _screenSize.width + _pBgSprite1->boundingBox().size.width/2;

	//calculate top/bottom limit (used for calculating the lanes)
	_bgTopBound = _screenSize.height - (UNSCALED_LANE_TOP_LIMIT * _scaleFactor);
	_bgBottomBound = UNSCALED_LANE_BOTTOM_LIMIT * _scaleFactor;

	//calculate the number of pixels per grid (based on height
	_pixelsPerGrid = (_bgTopBound - _bgBottomBound)/ MAP_GRID_HEIGHT;

	//setup image properties
	_pBgSprite1->setPosition( ccp( _pBgSprite1->boundingBox().size.width/2, _pBgSprite1->boundingBox().size.height/2) ); //first image start within screen

	_pBgSprite2 = CCSprite::create(pMapImageDirectory->getCString());
	_pBgSprite2->setScale(_scaleFactor);
	_pBgSprite2->setPosition( ccp( -_pBgSprite2->boundingBox().size.width/2, _pBgSprite2->boundingBox().size.height/2) ); //second image start outside of screen

}

/*!
    @function checkCollisionWithObstacles
    @param		rect:CCRect
				CCRect of the object to check with
	@param		sourecPoint: CCRect
				CCPoint of the object to check collision against
    @result		returns true, if the rect intersects any obstacles
    			returns false, if the rect does not intersect any obstacles
 */
bool GameMap::checkCollisionWithObstacles(CCRect rect, CCPoint sourcePoint)
{
	if (_obstacles == 0)
		return false;

	CCObject* obstacle = 0;
	CCARRAY_FOREACH(_obstacles, obstacle)
	{
		//update position of obstacles
		Obstacle* pObstacle = dynamic_cast<Obstacle*>(obstacle);
		//if collided
		if (isInSameLane(pObstacle->getPosition(), sourcePoint) && pObstacle->getRect().intersectsRect(rect))
		{
			return true;
		}
	}
	return false;
}

/*!
    @function isInSameLane
    @param		point1: CCPoint
    			Position of first object
	@param		point2: CCPoint
    			Position of second object
    @result		returns true if in same lane
    			false if different lanes
 */
bool GameMap::isInSameLane(CCPoint point1, CCPoint point2)
{
	float laneDiff = point1.y - point2.y;
	//absolute
	laneDiff *= laneDiff;

	//lane difference less than 5 pixels
	if (laneDiff < 25)
		return true;
	else
		return false;
}

/*!
    @function getNextGrid
    @param		isObstacle:bool
				whether to spawn an obstacle in the path
	@param		yCoord: float
				the current yCoord the function caller is at
    @result		returns a character state. if isObstacle = true, spawns an obstacle at the y coordinate given
 */
EnumCharacterState GameMap::getNextGrid(float yCoord, bool isObstacle)
{
	//character is to hit an obstacle
	if (isObstacle)
	{
		//spawn obstacle
		CCSprite* obstacle = Obstacle::createObstacle("Game/Obstacles/[Object] Obstacles (Small).png", _scaleFactor);

		//set its starting position to be outside of screen
		obstacle->setPosition( ccp(-obstacle->boundingBox().size.width/2, yCoord) );

		//add to screen and reference
		_screenHolder->addChild(obstacle, 0);
		_obstacles->addObject(obstacle);

		return HIT_OBSTACLE; //move straight into obstacle
	}
	else
	{
		//randomize to move up/down/straight
		int direction = arc4random() % 3;
		if (direction == 0) //straight
		{
			return RUN;
		}
		else if (direction == 1) //up
		{
			//do not exceed top bound
			if (yCoord + _pixelsPerGrid <= _bgTopBound)
			{
				return RUN_UP;
			}
			else
			{
				return RUN_DOWN;
			}
		}
		else //down
		{
			//do not exceed bottom bound
			if (yCoord - _pixelsPerGrid >= _bgBottomBound)
			{
				return RUN_DOWN;
			}
			else
			{
				return RUN_UP;
			}
		}
	}
}

void GameMap::update(float xSpeed)
{
	updateMap(xSpeed);
	updateObstacle(xSpeed);
}

/*!
    @function updateMap
    @param		xSpeed:float
				speed to shift the map at
    @result		scrolls the map according to the xspeed. automatically rearranges the map when the map moves out of screen
 */
void GameMap::updateMap(float xSpeed)
{
	//update background image position
	_pBgSprite1->setPositionX(_pBgSprite1->getPositionX() + xSpeed);
	_pBgSprite2->setPositionX(_pBgSprite2->getPositionX() + xSpeed);
	//check for scrolling
	if( _pBgSprite1->getPositionX() >= _bgEndPos )
		_pBgSprite1->setPositionX(_pBgSprite2->getPositionX() - _pBgSprite2->boundingBox().size.width);
	if( _pBgSprite2->getPositionX() >= _bgEndPos )
		_pBgSprite2->setPositionX(_pBgSprite1->getPositionX() - _pBgSprite1->boundingBox().size.width);
}

/*!
    @function updateObstacle
    @param		xSpeed:float
				speed to shift the obstacle at
	@result		shifts the obstacle according to the xspeed. automatically deletes the obstacle when it leaves the screen
 */
void GameMap::updateObstacle(float xSpeed)
{
	//update obstacle
	CCObject* obstacle = 0;
	CCARRAY_FOREACH(_obstacles, obstacle)
	{
		//update position of obstacles
		CCSprite* pObstacle = dynamic_cast<CCSprite*>(obstacle);
		pObstacle->setPositionX(pObstacle->getPositionX() + xSpeed);

		//check if the obstacles are out of screen
		if (pObstacle->getPositionX() - pObstacle->boundingBox().size.width/2 > _screenSize.width)
		{
			//remove obstacle
			_screenHolder->removeChild(pObstacle);
			_obstacles->removeObject(pObstacle,true);
		}
	}
}


CCPoint GameMap::getCharStartPoint()
{
	//left side of screen. middle lane
	return CCPoint(_screenSize.width/5, (MAP_GRID_HEIGHT/2)*_pixelsPerGrid + _pixelsPerGrid/2 + _bgBottomBound);
}

CCPoint GameMap::getRandomCatStartPoint()
{
	//get a random lane and its y position (middle of that lane)
	return getCatStartPoint(arc4random() % MAP_GRID_HEIGHT);
}

CCPoint GameMap::getCatStartPoint(int laneNumber)
{
	//fix the lane number bounds
	if (laneNumber < 0)
		laneNumber = 0;
	else if (laneNumber >= MAP_GRID_HEIGHT)
		laneNumber = MAP_GRID_HEIGHT-1;

	return ccp(_screenSize.width, laneNumber*_pixelsPerGrid + _pixelsPerGrid/2 + _bgBottomBound);
}


GameMap::~GameMap()
{
	//TODO: delete obstacle sprites cache

	//CLEAN UP OBSTACLES
	//loop through obstacles and cleanup
//	CCObject* obstacle = 0;
//	CCARRAY_FOREACH(_obstacles, obstacle)
//	{
//		CCSprite* pObstacle = dynamic_cast<CCSprite*>(obstacle);
//		_screenHolder->removeChild(pObstacle);
//		_obstacles->removeObject(pObstacle, true);
//	}
	_obstacles->release();
	_obstacles = NULL;

	//CLEAN UP BACKGROUND IMAGE
	//delete image
	_screenHolder->removeChild(_pBgSprite1,true);
	_screenHolder->removeChild(_pBgSprite2,true);
//	_pBgSprite1->release();
//	_pBgSprite2->release();
//	_pBgSprite1 = _pBgSprite2 = 0;

	_pBgSprite1 = 0;
	_pBgSprite2 = 0;
	_screenHolder = 0;
}

